﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Filter;
using COFE.Core.Lister;
using COFE.Core.Profile;
using System;
using COFE.Core.Utils;

namespace COFE.Core.Entry
{
    /// <summary>
    /// Represent a directory, please use COFE.Core.IDirectoryInfo interface instead.
    /// </summary>
    public class DirectoryInfoExA : FileSystemInfoExA, IInternalDirectoryInfoExA
    {
        #region Constructors

        public DirectoryInfoExA(string listerKey, IPropertyProvider primaryPropProvider)
            : base(listerKey, primaryPropProvider)
        {

        }



        #endregion

        #region Methods

        #region Enumerate and Lookup methods.


        public IEnumerable<T> EnumerateFSIs<T>(
            IEntryFilter[] entryFilters, IOptionFilter[] options, EntryConstructionFlags flags)
            where T : IFileSystemInfo
        {
            if (options.GetSearchSource() == SearchSourceType.None)
                return new List<T>();

            if (!entryFilters.Any(f => f is RootEntryFilter))
            {
                entryFilters = entryFilters.Concat(new List<IEntryFilter>() { 
                    new RootEntryFilter(this) }).ToArray();
            }

            IDirectoryLister dirLister = DirectoryLister;
            if (this.ParseName != ProfileRootDirectoryLister.ProfileRootParsablePath &&
                (flags & EntryConstructionFlags.EnableCache) != 0)
                dirLister = CacheDirectoryLister;

            if (dirLister is IDirectoryLister2)
            {
                IEnumerable<T> output = null;
                bool success = (dirLister as IDirectoryLister2).ListAll<T>(
                    this, entryFilters, options, flags, out output);
                if (success)
                    return output;
            }


            var expression = DirectoryInfoExA.enumerateFSIs<T>(
                dirLister, this, entryFilters, options, flags);
            expression = FilterUtils2.UpdateOptionFilterExpressions(expression, options);
            return expression;

        }

        private static IEnumerable<T> enumerateFSIs<T>(
            IDirectoryLister dirLister, IInternalDirectoryInfoExA parent,
            IEntryFilter[] entryFilters, IOptionFilter[] options,
            EntryConstructionFlags flags)
            where T : IFileSystemInfo
        {
            IEnumerator<IFileSystemInfo> etor =
                   dirLister.List(parent, flags).GetEnumerator();
            bool isReturnAllFSIs = typeof(T) == typeof(IFileSystemInfo) ||
                typeof(T) == typeof(IInternalFileSystemInfoExA);

            while (etor.MoveNext())
            {
                if ((entryFilters.MatchEntry(
                        (IInternalFileSystemInfoExA)etor.Current, options))
                        && (isReturnAllFSIs || etor.Current is T))
                    yield return (T)etor.Current;

                if (etor.Current is IInternalDirectoryInfoExA)
                {
                    IInternalDirectoryInfoExA di = etor.Current as IInternalDirectoryInfoExA;
                    if (entryFilters.ContinueLookup(di, options))
                    {
                        foreach (var entry in di.EnumerateFSIs<T>(entryFilters, options, flags))
                            yield return (T)entry;
                    }
                }

            }
        }


        public IFileSystemInfo this[string name, bool isFile]
        {
            get
            {
                if (isFile)
                    foreach (var fi in this.EnumerateFiles(name, SearchOptionEx.TopDirectoryOnly))
                        return fi;
                else
                    foreach (var fsi in this.EnumerateFileSystemInfos(name, SearchOptionEx.TopDirectoryOnly))
                        return fsi;

                return null;
            }
        }

        public IDirectoryInfo this[string name]
        {
            get { return this[name, false] as IDirectoryInfo; }
        }

        #endregion

        public void MoveTo(string destDirName)
        {
            throw new NotImplementedException();
        }

        public IDirectoryInfo CreateDirectory(string dirName)
        {
            return DirectoryController.CreateDirectory(this, dirName);
        }

        public IDirectoryInfo CreateVirtualDirectory(string dirName, string type)
        {
            var retVal = COFEShared.VolumeRegistrar.CreateVirtualDirectory(this, dirName, type);
            if (retVal != null)
                //This is to inform the cache that IFileBasedFS property needed to be updated.
                retVal.Refresh(); 
            return retVal;
        }



        public void AlertChildIsModified(IFileSystemInfo sender, IFileSystemInfo originalSender, ModifiedAction action)
        {
            //Notify DirectoryLister if current entries is FileBasedFS, otherwise no need to notify
            if (IsFileBasedFS)
                AlertModified(ModifiedAction.Changed);

            //If current entries is not filesystem, bubble up the notifications.
            if (Parent != null && !IsFileSystem)
                Parent.AlertChildIsModified(this, originalSender, action);
        }

        #endregion

        #region Data

        #endregion


        #region Public Properties


        public new IInternalDirectoryInfoExA InternalProperties { get { return this as IInternalDirectoryInfoExA; } }

        public IDirectoryLister CacheDirectoryLister { get { return getVolume().CacheDirectoryLister; } }
        public IDirectoryLister DirectoryLister { get { return getVolume().DirectoryLister; } }
        public IDirectoryController DirectoryController { get { return getVolume().DirectoryController; } }

        public IDirectoryInfo Root
        {
            get
            {
                IDirectoryInfo retVal = this;
                while (retVal.DirectoryType == DirectoryTypeEnum.dtFolder && retVal.Parent != null)
                    retVal = retVal.Parent;
                return retVal;
            }
        }

        #region PropertyProvider's Properties

        public IFileInfo FileBasedFSFile { get { return GetProperty(DirectoryInfoProperties.FileBasedFSFile).Value as IFileInfo; } }
        public bool IsFileBasedFS { get { return GetProperty(DirectoryInfoProperties.IsFileBasedFS).ValueAsBoolean; } }
        public DirectoryTypeEnum DirectoryType { get { return (DirectoryTypeEnum)GetProperty(DirectoryInfoProperties.DirectoryType).Value; } }
        public bool HasSubFolder { get { return this.TryGetProperty(DirectoryInfoProperties.HasSubFolders, false).ValueAsBoolean; } }
        public bool HasFiles { get { return this.TryGetProperty(DirectoryInfoProperties.HasFiles, false).ValueAsBoolean; } }

        #endregion

        #endregion









    }
}
